/*
 * Activiti Modeler component part of the Activiti project Copyright 2005-2014
 * Alfresco Software, Ltd. All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * 
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this library; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */
'use strict';

angular.module('activitiModeler').controller(
		'StencilController',
		['$rootScope', '$scope', '$http', '$modal', '$timeout',
				function($rootScope, $scope, $http, $modal, $timeout) {

					// Property window toggle state
					$scope.propertyWindowState = {
						'collapsed' : false
					};

					// Add reference to global header-config
					$scope.headerConfig = KISBPM.HEADER_CONFIG;

					$scope.propertyWindowState.toggle = function() {
						$scope.propertyWindowState.collapsed = !$scope.propertyWindowState.collapsed;
						$timeout(function() {
									jQuery(window).trigger('resize');
								});
					};

					// Code that is dependent on an initialised Editor is
					// wrapped in a promise for the editor
					$scope.editorFactory.promise.then(function() {

						/* Build stencil item list */

						// Build simple json representation of stencil set
						var stencilItemGroups = [];

						// Helper method: find a group in an array
						var findGroup = function(name, groupArray) {
							for (var index = 0; index < groupArray.length; index++) {
								if (groupArray[index].name === name) {
									return groupArray[index];
								}
							}
							return null;
						};

						// Helper method: add a new group to an array of groups
						var addGroup = function(groupName, groupArray) {
							var group = {
								name : groupName,
								items : [],
								paletteItems : [],
								groups : [],
								visible : true
							};
							groupArray.push(group);
							return group;
						};

						/*
						 * StencilSet items
						 */
						$http({
									method : 'GET',
									url : KISBPM.URL.getStencilSet()
								}).success(
								function(data, status, headers, config) {

									var quickMenuDefinition = ['UserTask',
											'EndNoneEvent', 'ExclusiveGateway',
											'CatchTimerEvent',
											'ThrowNoneEvent', 'TextAnnotation',
											'SequenceFlow', 'Association'];
									var ignoreForPaletteDefinition = [
											'SequenceFlow', 'MessageFlow',
											'Association', 'DataAssociation',
											'DataStore', 'SendTask'];
									var quickMenuItems = [];

									var morphRoles = [];
									for (var i = 0; i < data.rules.morphingRules.length; i++) {
										var role = data.rules.morphingRules[i].role;
										var roleItem = {
											'role' : role,
											'morphOptions' : []
										};
										morphRoles.push(roleItem);
									}

									// Check all received items
									for (var stencilIndex = 0; stencilIndex < data.stencils.length; stencilIndex++) {
										// Check if the root group is the
										// 'diagram' group. If so, this item
										// should not be shown.
										var currentGroupName = data.stencils[stencilIndex].groups[0];
										if (currentGroupName === 'Diagram'
												|| currentGroupName === 'Form') {
											continue; // go to next item
										}

										var removed = false;
										if (data.stencils[stencilIndex].removed) {
											removed = true;
										}

										var currentGroup = undefined;
										if (!removed) {
											// Check if this group already
											// exists. If not, we create a new
											// one

											if (currentGroupName !== null
													&& currentGroupName !== undefined
													&& currentGroupName.length > 0) {

												currentGroup = findGroup(
														currentGroupName,
														stencilItemGroups); // Find
																			// group
																			// in
																			// root
																			// groups
																			// array
												if (currentGroup === null) {
													currentGroup = addGroup(
															currentGroupName,
															stencilItemGroups);
												}

												// Add all child groups (if any)
												for (var groupIndex = 1; groupIndex < data.stencils[stencilIndex].groups.length; groupIndex++) {
													var childGroupName = data.stencils[stencilIndex].groups[groupIndex];
													var childGroup = findGroup(
															childGroupName,
															currentGroup.groups);
													if (childGroup === null) {
														childGroup = addGroup(
																childGroupName,
																currentGroup.groups);
													}

													// The current group
													// variable holds the parent
													// of the next group (if
													// any),
													// and is basically the last
													// element in the array of
													// groups defined in the
													// stencil item
													currentGroup = childGroup;

												}

											}
										}

										// Construct the stencil item
										var stencilItem = {
											'id' : data.stencils[stencilIndex].id,
											'name' : data.stencils[stencilIndex].title,
											'description' : data.stencils[stencilIndex].description,
											'icon' : data.stencils[stencilIndex].icon,
											'type' : data.stencils[stencilIndex].type,
											'roles' : data.stencils[stencilIndex].roles,
											'removed' : removed,
											'customIcon' : false,
											'canConnect' : false,
											'canConnectTo' : false,
											'canConnectAssociation' : false
										};

										if (data.stencils[stencilIndex].customIconId
												&& data.stencils[stencilIndex].customIconId > 0) {
											stencilItem.customIcon = true;
											stencilItem.icon = data.stencils[stencilIndex].customIconId;
										}

										if (!removed) {
											if (quickMenuDefinition
													.indexOf(stencilItem.id) >= 0) {
												quickMenuItems[quickMenuDefinition
														.indexOf(stencilItem.id)] = stencilItem;
											}
										}

										if (stencilItem.id === 'TextAnnotation'
												|| stencilItem.id === 'BoundaryCompensationEvent') {
											stencilItem.canConnectAssociation = true;
										}

										for (var i = 0; i < data.stencils[stencilIndex].roles.length; i++) {
											var stencilRole = data.stencils[stencilIndex].roles[i];
											if (stencilRole === 'sequence_start') {
												stencilItem.canConnect = true;
											} else if (stencilRole === 'sequence_end') {
												stencilItem.canConnectTo = true;
											}

											for (var j = 0; j < morphRoles.length; j++) {
												if (stencilRole === morphRoles[j].role) {
													if (!removed) {
														morphRoles[j].morphOptions
																.push(stencilItem);
													}
													stencilItem.morphRole = morphRoles[j].role;
													break;
												}
											}
										}

										if (currentGroup) {
											// Add the stencil item to the
											// correct group
											currentGroup.items
													.push(stencilItem);
											if (ignoreForPaletteDefinition
													.indexOf(stencilItem.id) < 0) {
												currentGroup.paletteItems
														.push(stencilItem);
											}

										} else {
											// It's a root stencil element
											if (!removed) {
												stencilItemGroups
														.push(stencilItem);
											}
										}
									}

									for (var i = 0; i < stencilItemGroups.length; i++) {
										if (stencilItemGroups[i].paletteItems
												&& stencilItemGroups[i].paletteItems.length == 0) {
											stencilItemGroups[i].visible = false;
										}
									}

									$scope.stencilItemGroups = stencilItemGroups;

									var containmentRules = [];
									for (var i = 0; i < data.rules.containmentRules.length; i++) {
										var rule = data.rules.containmentRules[i];
										containmentRules.push(rule);
									}
									$scope.containmentRules = containmentRules;

									// remove quick menu items which are not
									// available anymore due to custom pallette
									var availableQuickMenuItems = [];
									for (var i = 0; i < quickMenuItems.length; i++) {
										if (quickMenuItems[i]) {
											availableQuickMenuItems[availableQuickMenuItems.length] = quickMenuItems[i];
										}
									}

									$scope.quickMenuItems = availableQuickMenuItems;
									$scope.morphRoles = morphRoles;
								}).

						error(function(data, status, headers, config) {
							console
									.log('Something went wrong when fetching stencil items:'
											+ JSON.stringify(data));
						});

						/*
						 * Listen to selection change events: show properties
						 */
						$scope.editor.registerOnEvent(
								ORYX.CONFIG.EVENT_SELECTION_CHANGED, function(
										event) {
									var shapes = event.elements;
									var canvasSelected = false;
									if (shapes && shapes.length == 0) {
										shapes = [$scope.editor.getCanvas()];
										canvasSelected = true;
									}
									if (shapes && shapes.length > 0) {

										var selectedShape = shapes.first();
										var stencil = selectedShape
												.getStencil();

										if ($rootScope.selectedElementBeforeScrolling
												&& stencil.id()
														.indexOf('BPMNDiagram') !== -1) {
											// ignore canvas event because of
											// empty selection when scrolling
											// stops
											return;
										}

										if ($rootScope.selectedElementBeforeScrolling
												&& $rootScope.selectedElementBeforeScrolling
														.getId() === selectedShape
														.getId()) {
											$rootScope.selectedElementBeforeScrolling = null;
											return;
										}

										// Store previous selection
										$scope.previousSelectedShape = $scope.selectedShape;

										// Only do something if another element
										// is selected (Oryx fires this event
										// multiple times)
										if ($scope.selectedShape !== undefined
												&& $scope.selectedShape.getId() === selectedShape
														.getId()) {
											if ($rootScope.forceSelectionRefresh) {
												// Switch the flag again, this
												// run will force refresh
												$rootScope.forceSelectionRefresh = false;
											} else {
												// Selected the same element
												// again, no need to update
												// anything
												return;
											}
										}

										var selectedItem = {
											'title' : '',
											'properties' : []
										};

										if (canvasSelected) {
											selectedItem.auditData = {
												'author' : $scope.modelData.createdByUser,
												'createDate' : $scope.modelData.createDate
											};
										}

										// Gather properties of selected item
										var properties = stencil.properties();
										for (var i = 0; i < properties.length; i++) {
											var property = properties[i];
											if (property.popular() == false)
												continue;
											var key = property.prefix() + "-"
													+ property.id();

											if (key === 'oryx-name') {
												selectedItem.title = selectedShape.properties[key];
											}

											// First we check if there is a
											// config for 'key-type' and then
											// for 'type' alone
											var propertyConfig = KISBPM.PROPERTY_CONFIG[key
													+ '-' + property.type()];
											if (propertyConfig === undefined
													|| propertyConfig === null) {
												propertyConfig = KISBPM.PROPERTY_CONFIG[property
														.type()];
											}

											if (propertyConfig === undefined
													|| propertyConfig === null) {
												console
														.log('WARNING: no property configuration defined for '
																+ key
																+ ' of type '
																+ property
																		.type());
											} else {

												if (selectedShape.properties[key] === 'true') {
													selectedShape.properties[key] = true;
												}

												if (KISBPM.CONFIG.showRemovedProperties == false
														&& property.isHidden()) {
													continue;
												}

												var currentProperty = {
													'key' : key,
													'title' : property.title(),
													'type' : property.type(),
													'mode' : 'read',
													'hidden' : property
															.isHidden(),
													'value' : selectedShape.properties[key]
												};

												if ((currentProperty.type === 'complex' || currentProperty.type === 'multiplecomplex')
														&& currentProperty.value
														&& currentProperty.value.length > 0) {
													try {
														currentProperty.value = JSON
																.parse(currentProperty.value);
													} catch (err) {
														// ignore
													}
												}

												if (propertyConfig.readModeTemplateUrl !== undefined
														&& propertyConfig.readModeTemplateUrl !== null) {
													currentProperty.readModeTemplateUrl = propertyConfig.readModeTemplateUrl
															+ '?version='
															+ $rootScope.staticIncludeVersion;
												}
												if (propertyConfig.writeModeTemplateUrl !== null
														&& propertyConfig.writeModeTemplateUrl !== null) {
													currentProperty.writeModeTemplateUrl = propertyConfig.writeModeTemplateUrl
															+ '?version='
															+ $rootScope.staticIncludeVersion;
												}

												if (propertyConfig.templateUrl !== undefined
														&& propertyConfig.templateUrl !== null) {
													currentProperty.templateUrl = propertyConfig.templateUrl
															+ '?version='
															+ $rootScope.staticIncludeVersion;
													currentProperty.hasReadWriteMode = false;
												} else {
													currentProperty.hasReadWriteMode = true;
												}

												if (currentProperty.value === undefined
														|| currentProperty.value === null
														|| currentProperty.value.length == 0) {
													currentProperty.noValue = true;
												}

												selectedItem.properties
														.push(currentProperty);
											}
										}

										// Need to wrap this in an $apply block,
										// see
										// http://jimhoskins.com/2012/12/17/angularjs-and-apply.html
										$scope.safeApply(function() {
											$scope.selectedItem = selectedItem;
											$scope.selectedShape = selectedShape;
										});

									} else {
										$scope.safeApply(function() {
													$scope.selectedItem = {};
													$scope.selectedShape = null;
												});
									}
								});

						$scope.editor.registerOnEvent(
								ORYX.CONFIG.EVENT_SELECTION_CHANGED, function(
										event) {

									KISBPM.eventBus
											.dispatch(KISBPM.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS);
									var shapes = event.elements;

									if (shapes && shapes.length == 1) {

										var selectedShape = shapes.first();

										var a = $scope.editor.getCanvas().node
												.getScreenCTM();

										var absoluteXY = selectedShape
												.absoluteXY();

										absoluteXY.x *= a.a;
										absoluteXY.y *= a.d;

										var additionalIEZoom = 1;
										if (!isNaN(screen.logicalXDPI)
												&& !isNaN(screen.systemXDPI)) {
											var ua = navigator.userAgent;
											if (ua.indexOf('MSIE') >= 0) {
												// IE 10 and below
												var zoom = Math
														.round((screen.deviceXDPI / screen.logicalXDPI)
																* 100);
												if (zoom !== 100) {
													additionalIEZoom = zoom
															/ 100
												}
											}
										}

										if (additionalIEZoom === 1) {
											absoluteXY.y = absoluteXY.y
													- jQuery("#canvasSection")
															.offset().top + 5;
											absoluteXY.x = absoluteXY.x
													- jQuery("#canvasSection")
															.offset().left;

										} else {
											var canvasOffsetLeft = jQuery("#canvasSection")
													.offset().left;
											var canvasScrollLeft = jQuery("#canvasSection")
													.scrollLeft();
											var canvasScrollTop = jQuery("#canvasSection")
													.scrollTop();

											var offset = a.e
													- (canvasOffsetLeft * additionalIEZoom);
											var additionaloffset = 0;
											if (offset > 10) {
												additionaloffset = (offset / additionalIEZoom)
														- offset;
											}
											absoluteXY.y = absoluteXY.y
													- (jQuery("#canvasSection")
															.offset().top * additionalIEZoom)
													+ 5
													+ ((canvasScrollTop * additionalIEZoom) - canvasScrollTop);
											absoluteXY.x = absoluteXY.x
													- (canvasOffsetLeft * additionalIEZoom)
													+ additionaloffset
													+ ((canvasScrollLeft * additionalIEZoom) - canvasScrollLeft);
										}

										var bounds = new ORYX.Core.Bounds(a.e
														+ absoluteXY.x, a.f
														+ absoluteXY.y, a.e
														+ absoluteXY.x
														+ a.a
														* selectedShape.bounds
																.width(), a.f
														+ absoluteXY.y
														+ a.d
														* selectedShape.bounds
																.height());
										var shapeXY = bounds.upperLeft();

										var stencilItem = $scope
												.getStencilItemById(selectedShape
														.getStencil()
														.idWithoutNs());
										var morphShapes = [];
										if (stencilItem
												&& stencilItem.morphRole) {
											for (var i = 0; i < $scope.morphRoles.length; i++) {
												if ($scope.morphRoles[i].role === stencilItem.morphRole) {
													morphShapes = $scope.morphRoles[i].morphOptions;
												}
											}
										}

										var x = shapeXY.x;
										if (bounds.width() < 48) {
											x -= 24;
										}

										if (morphShapes
												&& morphShapes.length > 0) {
											// In case the element is not wide
											// enough, start the 2
											// bottom-buttons more to the left
											// to prevent overflow in the
											// right-menu
											var morphButton = document
													.getElementById('morph-button');
											morphButton.style.display = "block";
											morphButton.style.left = x + 24
													+ 'px';
											morphButton.style.top = (shapeXY.y
													+ bounds.height() + 2)
													+ 'px';
										}

										var deleteButton = document
												.getElementById('delete-button');
										deleteButton.style.display = "block";
										deleteButton.style.left = x + 'px';
										deleteButton.style.top = (shapeXY.y
												+ bounds.height() + 2)
												+ 'px';

										if (stencilItem
												&& (stencilItem.canConnect || stencilItem.canConnectAssociation)) {
											var quickButtonCounter = 0;
											var quickButtonX = shapeXY.x
													+ bounds.width() + 5;
											var quickButtonY = shapeXY.y;
											jQuery('.Oryx_button').each(
													function(i, obj) {
														if (obj.id !== 'morph-button'
																&& obj.id != 'delete-button') {
															quickButtonCounter++;
															if (quickButtonCounter > 3) {
																quickButtonX = shapeXY.x
																		+ bounds
																				.width()
																		+ 5;
																quickButtonY += 24;
																quickButtonCounter = 1;

															} else if (quickButtonCounter > 1) {
																quickButtonX += 24;
															}
															obj.style.display = "block";
															obj.style.left = quickButtonX
																	+ 'px';
															obj.style.top = quickButtonY
																	+ 'px';
														}
													});
										}
									}
								});

						if (!$rootScope.stencilInitialized) {
							KISBPM.eventBus
									.addListener(
											KISBPM.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS,
											function(event) {
												jQuery('.Oryx_button').each(
														function(i, obj) {
															obj.style.display = "none";
														});
											});

							/*
							 * Listen to property updates and act upon them
							 */
							KISBPM.eventBus
									.addListener(
											KISBPM.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED,
											function(event) {
												if (event.property
														&& event.property.key) {
													// If the name property is
													// been updated, we also
													// need to change the title
													// of the currently selected
													// item
													if (event.property.key === 'oryx-name'
															&& $scope.selectedItem !== undefined
															&& $scope.selectedItem !== null) {
														$scope.selectedItem.title = event.newValue;
													}

													// Update "no value" flag
													event.property.noValue = (event.property.value === undefined
															|| event.property.value === null || event.property.value.length == 0);
												}
											});

							$rootScope.stencilInitialized = true;
						}

						$scope.morphShape = function() {
							$scope.safeApply(function() {

								var shapes = $rootScope.editor.getSelection();
								if (shapes && shapes.length == 1) {
									$rootScope.currentSelectedShape = shapes
											.first();
									var stencilItem = $scope
											.getStencilItemById($rootScope.currentSelectedShape
													.getStencil().idWithoutNs());
									var morphShapes = [];
									for (var i = 0; i < $scope.morphRoles.length; i++) {
										if ($scope.morphRoles[i].role === stencilItem.morphRole) {
											morphShapes = $scope.morphRoles[i].morphOptions
													.slice();
										}
									}

									// Method to open shape select dialog (used
									// later on)
									var showSelectShapeDialog = function() {
										$rootScope.morphShapes = morphShapes;
										$modal({
											backdrop : false,
											keyboard : true,
											template : 'editor-app/popups/select-shape.html?version='
													+ Date.now()
										});
									};

									showSelectShapeDialog();
								}
							});
						};

						$scope.deleteShape = function() {
							KISBPM.TOOLBAR.ACTIONS.deleteItem({
										'$scope' : $scope
									});
						};

						$scope.quickAddItem = function(newItemId) {
							$scope.safeApply(function() {

								var shapes = $rootScope.editor.getSelection();
								if (shapes && shapes.length == 1) {
									$rootScope.currentSelectedShape = shapes
											.first();

									var containedStencil = undefined;
									var stencilSets = $scope.editor
											.getStencilSets().values();
									for (var i = 0; i < stencilSets.length; i++) {
										var stencilSet = stencilSets[i];
										var nodes = stencilSet.nodes();
										for (var j = 0; j < nodes.length; j++) {
											if (nodes[j].idWithoutNs() === newItemId) {
												containedStencil = nodes[j];
												break;
											}
										}
									}

									if (!containedStencil)
										return;

									var option = {
										type : $scope.currentSelectedShape
												.getStencil().namespace()
												+ newItemId,
										namespace : $scope.currentSelectedShape
												.getStencil().namespace()
									};
									option['connectedShape'] = $rootScope.currentSelectedShape;
									option['parent'] = $rootScope.currentSelectedShape.parent;
									option['containedStencil'] = containedStencil;

									var args = {
										sourceShape : $rootScope.currentSelectedShape,
										targetStencil : containedStencil
									};
									var targetStencil = $scope.editor
											.getRules().connectMorph(args);
									if (!targetStencil) {
										return;
									}// Check if there can be a target shape
									option['connectingType'] = targetStencil
											.id();

									var command = new KISBPM.CreateCommand(
											option, undefined, undefined,
											$scope.editor);

									$scope.editor.executeCommands([command]);
								}
							});
						};

					}); // end of $scope.editorFactory.promise block

					/* Click handler for clicking a property */
					$scope.propertyClicked = function(index) {
						if (!$scope.selectedItem.properties[index].hidden) {
							$scope.selectedItem.properties[index].mode = "write";
						}
					};

					/* Helper method to retrieve the template url for a property */
					$scope.getPropertyTemplateUrl = function(index) {
						return $scope.selectedItem.properties[index].templateUrl;
					};
					$scope.getPropertyReadModeTemplateUrl = function(index) {
						return $scope.selectedItem.properties[index].readModeTemplateUrl;
					};
					$scope.getPropertyWriteModeTemplateUrl = function(index) {
						return $scope.selectedItem.properties[index].writeModeTemplateUrl;
					};

					/*
					 * Method available to all sub controllers (for property
					 * controllers) to update the internal Oryx model
					 */
					$scope.updatePropertyInModel = function(property, shapeId) {

						var shape = $scope.selectedShape;
						// Some updates may happen when selected shape is
						// already changed, so when an additional
						// shapeId is supplied, we need to make sure the correct
						// shape is updated (current or previous)
						if (shapeId) {
							if (shape.id != shapeId
									&& $scope.previousSelectedShape
									&& $scope.previousSelectedShape.id == shapeId) {
								shape = $scope.previousSelectedShape;
							} else {
								shape = null;
							}
						}

						if (!shape) {
							// When no shape is selected, or no shape is found
							// for the alternative
							// shape ID, do nothing
							return;
						}
						var key = property.key;
						var newValue = property.value;
						var oldValue = shape.properties[key];

						if (newValue != oldValue) {
							var commandClass = ORYX.Core.Command.extend({
										construct : function() {
											this.key = key;
											this.oldValue = oldValue;
											this.newValue = newValue;
											this.shape = shape;
											this.facade = $scope.editor;
										},
										execute : function() {
											this.shape.setProperty(this.key,
													this.newValue);
											this.facade.getCanvas().update();
											this.facade.updateSelection();
										},
										rollback : function() {
											this.shape.setProperty(this.key,
													this.oldValue);
											this.facade.getCanvas().update();
											this.facade.updateSelection();
										}
									});
							// Instantiate the class
							var command = new commandClass();

							// Execute the command
							$scope.editor.executeCommands([command]);
							$scope.editor.handleEvents({
								type : ORYX.CONFIG.EVENT_PROPWINDOW_PROP_CHANGED,
								elements : [shape],
								key : key
							});

							// Switch the property back to read mode, now the
							// update is done
							property.mode = 'read';

							// Fire event to all who is interested
							// Fire event to all who want to know about this
							var event = {
								type : KISBPM.eventBus.EVENT_TYPE_PROPERTY_VALUE_CHANGED,
								property : property,
								oldValue : oldValue,
								newValue : newValue
							};
							KISBPM.eventBus.dispatch(event.type, event);
						} else {
							// Switch the property back to read mode, no update
							// was needed
							property.mode = 'read';
						}

					};

					/**
					 * Helper method that searches a group for an item with the
					 * given id. If not found, will return undefined.
					 */
					$scope.findStencilItemInGroup = function(stencilItemId,
							group) {

						var item;

						// Check all items directly in this group
						for (var j = 0; j < group.items.length; j++) {
							item = group.items[j];
							if (item.id === stencilItemId) {
								return item;
							}
						}

						// Check the child groups
						if (group.groups && group.groups.length > 0) {
							for (var k = 0; k < group.groups.length; k++) {
								item = $scope.findStencilItemInGroup(
										stencilItemId, group.groups[k]);
								if (item) {
									return item;
								}
							}
						}

						return undefined;
					};

					/**
					 * Helper method to find a stencil item.
					 */
					$scope.getStencilItemById = function(stencilItemId) {
						for (var i = 0; i < $scope.stencilItemGroups.length; i++) {
							var element = $scope.stencilItemGroups[i];

							// Real group
							if (element.items !== null
									&& element.items !== undefined) {
								var item = $scope.findStencilItemInGroup(
										stencilItemId, element);
								if (item) {
									return item;
								}
							} else { // Root stencil item
								if (element.id === stencilItemId) {
									return element;
								}
							}
						}
						return undefined;
					};

					/*
					 * DRAG AND DROP FUNCTIONALITY
					 */

					$scope.dropCallback = function(event, ui) {

						$scope.editor.handleEvents({
									type : ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
									highlightId : "shapeRepo.attached"
								});
						$scope.editor.handleEvents({
									type : ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
									highlightId : "shapeRepo.added"
								});

						$scope.editor.handleEvents({
									type : ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
									highlightId : "shapeMenu"
								});

						KISBPM.eventBus
								.dispatch(KISBPM.eventBus.EVENT_TYPE_HIDE_SHAPE_BUTTONS);

						if ($scope.dragCanContain) {

							var item = $scope
									.getStencilItemById(ui.draggable[0].id);

							var pos = {
								x : event.pageX,
								y : event.pageY
							};

							var additionalIEZoom = 1;
							if (!isNaN(screen.logicalXDPI)
									&& !isNaN(screen.systemXDPI)) {
								var ua = navigator.userAgent;
								if (ua.indexOf('MSIE') >= 0) {
									// IE 10 and below
									var zoom = Math
											.round((screen.deviceXDPI / screen.logicalXDPI)
													* 100);
									if (zoom !== 100) {
										additionalIEZoom = zoom / 100;
									}
								}
							}

							var screenCTM = $scope.editor.getCanvas().node
									.getScreenCTM();

							// Correcting the UpperLeft-Offset
							pos.x -= (screenCTM.e / additionalIEZoom);
							pos.y -= (screenCTM.f / additionalIEZoom);
							// Correcting the Zoom-Factor
							pos.x /= screenCTM.a;
							pos.y /= screenCTM.d;

							// Correcting the ScrollOffset
							pos.x -= document.documentElement.scrollLeft;
							pos.y -= document.documentElement.scrollTop;

							var parentAbs = $scope.dragCurrentParent
									.absoluteXY();
							pos.x -= parentAbs.x;
							pos.y -= parentAbs.y;

							var containedStencil = undefined;
							var stencilSets = $scope.editor.getStencilSets()
									.values();
							for (var i = 0; i < stencilSets.length; i++) {
								var stencilSet = stencilSets[i];
								var nodes = stencilSet.nodes();
								for (var j = 0; j < nodes.length; j++) {
									if (nodes[j].idWithoutNs() === ui.draggable[0].id) {
										containedStencil = nodes[j];
										break;
									}
								}

								if (!containedStencil) {
									var edges = stencilSet.edges();
									for (var j = 0; j < edges.length; j++) {
										if (edges[j].idWithoutNs() === ui.draggable[0].id) {
											containedStencil = edges[j];
											break;
										}
									}
								}
							}

							if (!containedStencil)
								return;

							if ($scope.quickMenu) {
								var shapes = $scope.editor.getSelection();
								if (shapes && shapes.length == 1) {
									var currentSelectedShape = shapes.first();

									var option = {};
									option.type = currentSelectedShape
											.getStencil().namespace()
											+ ui.draggable[0].id;
									option.namespace = currentSelectedShape
											.getStencil().namespace();
									option.connectedShape = currentSelectedShape;
									option.parent = $scope.dragCurrentParent;
									option.containedStencil = containedStencil;

									// If the ctrl key is not pressed,
									// snapp the new shape to the center
									// if it is near to the center of the other
									// shape
									if (!event.ctrlKey) {
										// Get the center of the shape
										var cShape = currentSelectedShape.bounds
												.center();
										// Snapp +-20 Pixel horizontal to the
										// center
										if (20 > Math.abs(cShape.x - pos.x)) {
											pos.x = cShape.x;
										}
										// Snapp +-20 Pixel vertical to the
										// center
										if (20 > Math.abs(cShape.y - pos.y)) {
											pos.y = cShape.y;
										}
									}

									option.position = pos;

									if (containedStencil.idWithoutNs() !== 'SequenceFlow'
											&& containedStencil.idWithoutNs() !== 'Association'
											&& containedStencil.idWithoutNs() !== 'MessageFlow'
											&& containedStencil.idWithoutNs() !== 'DataAssociation') {
										var args = {
											sourceShape : currentSelectedShape,
											targetStencil : containedStencil
										};
										var targetStencil = $scope.editor
												.getRules().connectMorph(args);
										if (!targetStencil) {
											return;
										}// Check if there can be a target
											// shape
										option.connectingType = targetStencil
												.id();
									}

									var command = new KISBPM.CreateCommand(
											option, $scope.dropTargetElement,
											pos, $scope.editor);

									$scope.editor.executeCommands([command]);
								}
							} else {
								var canAttach = false;
								if (containedStencil.idWithoutNs() === 'BoundaryErrorEvent'
										|| containedStencil.idWithoutNs() === 'BoundaryTimerEvent'
										|| containedStencil.idWithoutNs() === 'BoundarySignalEvent'
										|| containedStencil.idWithoutNs() === 'BoundaryMessageEvent'
										|| containedStencil.idWithoutNs() === 'BoundaryCancelEvent'
										|| containedStencil.idWithoutNs() === 'BoundaryCompensationEvent') {
									// Modify position, otherwise boundary event
									// will get position related to left corner
									// of the canvas instead of the container
									pos = $scope.editor.eventCoordinates(event);
									canAttach = true;
								}

								var option = {};
								option['type'] = $scope.modelData.model.stencilset.namespace
										+ item.id;
								option['namespace'] = $scope.modelData.model.stencilset.namespace;
								option['position'] = pos;
								option['parent'] = $scope.dragCurrentParent;

								var commandClass = ORYX.Core.Command.extend({
									construct : function(option, dockedShape,
											canAttach, position, facade) {
										this.option = option;
										this.docker = null;
										this.dockedShape = dockedShape;
										this.dockedShapeParent = dockedShape.parent
												|| facade.getCanvas();
										this.position = position;
										this.facade = facade;
										this.selection = this.facade
												.getSelection();
										this.shape = null;
										this.parent = null;
										this.canAttach = canAttach;
									},
									execute : function() {
										if (!this.shape) {
											this.shape = this.facade
													.createShape(option);
											this.parent = this.shape.parent;
										} else if (this.parent) {
											this.parent.add(this.shape);
										}

										if (this.canAttach
												&& this.shape.dockers
												&& this.shape.dockers.length) {
											this.docker = this.shape.dockers[0];

											this.dockedShapeParent
													.add(this.docker.parent);

											// Set the Docker to the new Shape
											this.docker
													.setDockedShape(undefined);
											this.docker.bounds
													.centerMoveTo(this.position);
											if (this.dockedShape !== this.facade
													.getCanvas()) {
												this.docker
														.setDockedShape(this.dockedShape);
											}
											this.facade
													.setSelection([this.docker.parent]);
										}

										this.facade.getCanvas().update();
										this.facade.updateSelection();

									},
									rollback : function() {
										if (this.shape) {
											this.facade
													.setSelection(this.selection
															.without(this.shape));
											this.facade.deleteShape(this.shape);
										}
										if (this.canAttach && this.docker) {
											this.docker
													.setDockedShape(undefined);
										}
										this.facade.getCanvas().update();
										this.facade.updateSelection();

									}
								});

								// Update canvas
								var command = new commandClass(option,
										$scope.dragCurrentParent, canAttach,
										pos, $scope.editor);
								$scope.editor.executeCommands([command]);

								// Fire event to all who want to know about this
								var dropEvent = {
									type : KISBPM.eventBus.EVENT_TYPE_ITEM_DROPPED,
									droppedItem : item,
									position : pos
								};
								KISBPM.eventBus.dispatch(dropEvent.type,
										dropEvent);
							}
						}

						$scope.dragCurrentParent = undefined;
						$scope.dragCurrentParentId = undefined;
						$scope.dragCurrentParentStencil = undefined;
						$scope.dragCanContain = undefined;
						$scope.quickMenu = undefined;
						$scope.dropTargetElement = undefined;
					};

					$scope.overCallback = function(event, ui) {
						$scope.dragModeOver = true;
					};

					$scope.outCallback = function(event, ui) {
						$scope.dragModeOver = false;
					};

					$scope.startDragCallback = function(event, ui) {
						$scope.dragModeOver = false;
						$scope.quickMenu = false;
						if (!ui.helper.hasClass('stencil-item-dragged')) {
							ui.helper.addClass('stencil-item-dragged');
						}
					};

					$scope.startDragCallbackQuickMenu = function(event, ui) {
						$scope.dragModeOver = false;
						$scope.quickMenu = true;
					};

					$scope.dragCallback = function(event, ui) {

						if ($scope.dragModeOver != false) {

							var coord = $scope.editor.eventCoordinatesXY(
									event.pageX, event.pageY);

							var additionalIEZoom = 1;
							if (!isNaN(screen.logicalXDPI)
									&& !isNaN(screen.systemXDPI)) {
								var ua = navigator.userAgent;
								if (ua.indexOf('MSIE') >= 0) {
									// IE 10 and below
									var zoom = Math
											.round((screen.deviceXDPI / screen.logicalXDPI)
													* 100);
									if (zoom !== 100) {
										additionalIEZoom = zoom / 100
									}
								}
							}

							if (additionalIEZoom !== 1) {
								coord.x = coord.x / additionalIEZoom;
								coord.y = coord.y / additionalIEZoom;
							}

							var aShapes = $scope.editor.getCanvas()
									.getAbstractShapesAtPosition(coord);

							if (aShapes.length <= 0) {
								if (event.helper) {
									$scope.dragCanContain = false;
									return false;
								}
							}

							if (aShapes[0] instanceof ORYX.Core.Canvas) {
								$scope.editor.getCanvas()
										.setHightlightStateBasedOnX(coord.x);
							}

							if (aShapes.length == 1
									&& aShapes[0] instanceof ORYX.Core.Canvas) {
								var parentCandidate = aShapes[0];

								$scope.dragCanContain = true;
								$scope.dragCurrentParent = parentCandidate;
								$scope.dragCurrentParentId = parentCandidate.id;

								$scope.editor.handleEvents({
											type : ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
											highlightId : "shapeRepo.attached"
										});
								$scope.editor.handleEvents({
											type : ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
											highlightId : "shapeRepo.added"
										});
								return false;
							} else {
								var item = $scope
										.getStencilItemById(event.target.id);

								var parentCandidate = aShapes.reverse().find(
										function(candidate) {
											return (candidate instanceof ORYX.Core.Canvas
													|| candidate instanceof ORYX.Core.Node || candidate instanceof ORYX.Core.Edge);
										});

								if (!parentCandidate) {
									$scope.dragCanContain = false;
									return false;
								}

								if (item.type === "node") {

									// check if the draggable is a boundary
									// event and the parent an Activity
									var _canContain = false;
									var parentStencilId = parentCandidate
											.getStencil().id();

									if ($scope.dragCurrentParentId
											&& $scope.dragCurrentParentId === parentCandidate.id) {
										return false;
									}

									var parentItem = $scope
											.getStencilItemById(parentCandidate
													.getStencil().idWithoutNs());
									if (parentItem.roles.indexOf("Activity") > -1) {
										if (item.roles
												.indexOf("IntermediateEventOnActivityBoundary") > -1) {
											_canContain = true;
										}
									} else if (parentCandidate.getStencil()
											.idWithoutNs() === 'Pool') {
										if (item.id === 'Lane') {
											_canContain = true;
										}
									}

									if (_canContain) {
										$scope.editor.handleEvents({
											type : ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
											highlightId : "shapeRepo.attached",
											elements : [parentCandidate],
											style : ORYX.CONFIG.SELECTION_HIGHLIGHT_STYLE_RECTANGLE,
											color : ORYX.CONFIG.SELECTION_VALID_COLOR
										});

										$scope.editor.handleEvents({
											type : ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
											highlightId : "shapeRepo.added"
										});
									} else {
										for (var i = 0; i < $scope.containmentRules.length; i++) {
											var rule = $scope.containmentRules[i];
											if (rule.role === parentItem.id) {
												for (var j = 0; j < rule.contains.length; j++) {
													if (item.roles
															.indexOf(rule.contains[j]) > -1) {
														_canContain = true;
														break;
													}
												}

												if (_canContain) {
													break;
												}
											}
										}

										// Show Highlight
										$scope.editor.handleEvents({
											type : ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
											highlightId : 'shapeRepo.added',
											elements : [parentCandidate],
											color : _canContain
													? ORYX.CONFIG.SELECTION_VALID_COLOR
													: ORYX.CONFIG.SELECTION_INVALID_COLOR
										});

										$scope.editor.handleEvents({
											type : ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
											highlightId : "shapeRepo.attached"
										});
									}

									$scope.dragCurrentParent = parentCandidate;
									$scope.dragCurrentParentId = parentCandidate.id;
									$scope.dragCurrentParentStencil = parentStencilId;
									$scope.dragCanContain = _canContain;

								} else {
									var canvasCandidate = $scope.editor
											.getCanvas();
									var canConnect = false;

									var targetStencil = $scope
											.getStencilItemById(parentCandidate
													.getStencil().idWithoutNs());
									if (targetStencil) {
										var associationConnect = false;
										if (stencil.idWithoutNs() === 'Association'
												&& (curCan.getStencil()
														.idWithoutNs() === 'TextAnnotation' || curCan
														.getStencil()
														.idWithoutNs() === 'BoundaryCompensationEvent')) {
											associationConnect = true;
										} else if (stencil.idWithoutNs() === 'DataAssociation'
												&& curCan.getStencil()
														.idWithoutNs() === 'DataStore') {
											associationConnect = true;
										}

										if (targetStencil.canConnectTo
												|| associationConnect) {
											canConnect = true;
										}
									}

									// Edge
									$scope.dragCurrentParent = canvasCandidate;
									$scope.dragCurrentParentId = canvasCandidate.id;
									$scope.dragCurrentParentStencil = canvasCandidate
											.getStencil().id();
									$scope.dragCanContain = canConnect;

									// Show Highlight
									$scope.editor.handleEvents({
										type : ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
										highlightId : 'shapeRepo.added',
										elements : [canvasCandidate],
										color : ORYX.CONFIG.SELECTION_VALID_COLOR
									});

									$scope.editor.handleEvents({
										type : ORYX.CONFIG.EVENT_HIGHLIGHT_HIDE,
										highlightId : "shapeRepo.attached"
									});
								}
							}
						}
					};

					$scope.dragCallbackQuickMenu = function(event, ui) {

						if ($scope.dragModeOver != false) {
							var coord = $scope.editor.eventCoordinatesXY(
									event.pageX, event.pageY);

							var additionalIEZoom = 1;
							if (!isNaN(screen.logicalXDPI)
									&& !isNaN(screen.systemXDPI)) {
								var ua = navigator.userAgent;
								if (ua.indexOf('MSIE') >= 0) {
									// IE 10 and below
									var zoom = Math
											.round((screen.deviceXDPI / screen.logicalXDPI)
													* 100);
									if (zoom !== 100) {
										additionalIEZoom = zoom / 100
									}
								}
							}

							if (additionalIEZoom !== 1) {
								coord.x = coord.x / additionalIEZoom;
								coord.y = coord.y / additionalIEZoom;
							}

							var aShapes = $scope.editor.getCanvas()
									.getAbstractShapesAtPosition(coord);

							if (aShapes.length <= 0) {
								if (event.helper) {
									$scope.dragCanContain = false;
									return false;
								}
							}

							if (aShapes[0] instanceof ORYX.Core.Canvas) {
								$scope.editor.getCanvas()
										.setHightlightStateBasedOnX(coord.x);
							}

							var stencil = undefined;
							var stencilSets = $scope.editor.getStencilSets()
									.values();
							for (var i = 0; i < stencilSets.length; i++) {
								var stencilSet = stencilSets[i];
								var nodes = stencilSet.nodes();
								for (var j = 0; j < nodes.length; j++) {
									if (nodes[j].idWithoutNs() === event.target.id) {
										stencil = nodes[j];
										break;
									}
								}

								if (!stencil) {
									var edges = stencilSet.edges();
									for (var j = 0; j < edges.length; j++) {
										if (edges[j].idWithoutNs() === event.target.id) {
											stencil = edges[j];
											break;
										}
									}
								}
							}

							var candidate = aShapes.last();

							var isValid = false;
							if (stencil.type() === "node") {
								// check containment rules
								var canContain = $scope.editor.getRules()
										.canContain({
													containingShape : candidate,
													containedStencil : stencil
												});

								var parentCandidate = aShapes.reverse().find(
										function(candidate) {
											return (candidate instanceof ORYX.Core.Canvas
													|| candidate instanceof ORYX.Core.Node || candidate instanceof ORYX.Core.Edge);
										});

								if (!parentCandidate) {
									$scope.dragCanContain = false;
									return false;
								}

								$scope.dragCurrentParent = parentCandidate;
								$scope.dragCurrentParentId = parentCandidate.id;
								$scope.dragCurrentParentStencil = parentCandidate
										.getStencil().id();
								$scope.dragCanContain = canContain;
								$scope.dropTargetElement = parentCandidate;
								isValid = canContain;

							} else { // Edge

								var shapes = $scope.editor.getSelection();
								if (shapes && shapes.length == 1) {
									var currentSelectedShape = shapes.first();
									var curCan = candidate;
									var canConnect = false;

									var targetStencil = $scope
											.getStencilItemById(curCan
													.getStencil().idWithoutNs());
									if (targetStencil) {
										var associationConnect = false;
										if (stencil.idWithoutNs() === 'Association'
												&& (curCan.getStencil()
														.idWithoutNs() === 'TextAnnotation' || curCan
														.getStencil()
														.idWithoutNs() === 'BoundaryCompensationEvent')) {
											associationConnect = true;
										} else if (stencil.idWithoutNs() === 'DataAssociation'
												&& curCan.getStencil()
														.idWithoutNs() === 'DataStore') {
											associationConnect = true;
										}

										if (targetStencil.canConnectTo
												|| associationConnect) {
											while (!canConnect
													&& curCan
													&& !(curCan instanceof ORYX.Core.Canvas)) {
												candidate = curCan;
												// check connection rules
												canConnect = $scope.editor
														.getRules().canConnect(
																{
																	sourceShape : currentSelectedShape,
																	edgeStencil : stencil,
																	targetShape : curCan
																});
												curCan = curCan.parent;
											}
										}
									}
									var parentCandidate = $scope.editor
											.getCanvas();

									isValid = canConnect;
									$scope.dragCurrentParent = parentCandidate;
									$scope.dragCurrentParentId = parentCandidate.id;
									$scope.dragCurrentParentStencil = parentCandidate
											.getStencil().id();
									$scope.dragCanContain = canConnect;
									$scope.dropTargetElement = candidate;
								}

							}

							$scope.editor.handleEvents({
										type : ORYX.CONFIG.EVENT_HIGHLIGHT_SHOW,
										highlightId : 'shapeMenu',
										elements : [candidate],
										color : isValid
												? ORYX.CONFIG.SELECTION_VALID_COLOR
												: ORYX.CONFIG.SELECTION_INVALID_COLOR
									});
						}
					};

				}]);

var KISBPM = KISBPM || {};
// create command for undo/redo
KISBPM.CreateCommand = ORYX.Core.Command.extend({
	construct : function(option, currentReference, position, facade) {
		this.option = option;
		this.currentReference = currentReference;
		this.position = position;
		this.facade = facade;
		this.shape;
		this.edge;
		this.targetRefPos;
		this.sourceRefPos;
		/*
		 * clone options parameters
		 */
		this.connectedShape = option.connectedShape;
		this.connectingType = option.connectingType;
		this.namespace = option.namespace;
		this.type = option.type;
		this.containedStencil = option.containedStencil;
		this.parent = option.parent;
		this.currentReference = currentReference;
		this.shapeOptions = option.shapeOptions;
	},
	execute : function() {

		if (this.shape) {
			if (this.shape instanceof ORYX.Core.Node) {
				this.parent.add(this.shape);
				if (this.edge) {
					this.facade.getCanvas().add(this.edge);
					this.edge.dockers.first()
							.setDockedShape(this.connectedShape);
					this.edge.dockers.first()
							.setReferencePoint(this.sourceRefPos);
					this.edge.dockers.last().setDockedShape(this.shape);
					this.edge.dockers.last()
							.setReferencePoint(this.targetRefPos);
				}

				this.facade.setSelection([this.shape]);

			} else if (this.shape instanceof ORYX.Core.Edge) {
				this.facade.getCanvas().add(this.shape);
				this.shape.dockers.first().setDockedShape(this.connectedShape);
				this.shape.dockers.first().setReferencePoint(this.sourceRefPos);
			}
		} else {
			this.shape = this.facade.createShape(this.option);
			this.edge = (!(this.shape instanceof ORYX.Core.Edge)) ? this.shape
					.getIncomingShapes().first() : undefined;
		}

		if (this.currentReference && this.position) {

			if (this.shape instanceof ORYX.Core.Edge) {

				if (!(this.currentReference instanceof ORYX.Core.Canvas)) {
					this.shape.dockers.last()
							.setDockedShape(this.currentReference);

					if (this.currentReference.getStencil().idWithoutNs() === 'TextAnnotation') {
						var midpoint = {};
						midpoint.x = 0;
						midpoint.y = this.currentReference.bounds.height() / 2;
						this.shape.dockers.last().setReferencePoint(midpoint);
					} else {
						this.shape.dockers.last()
								.setReferencePoint(this.currentReference.bounds
										.midPoint());
					}
				} else {
					this.shape.dockers.last().bounds
							.centerMoveTo(this.position);
				}
				this.sourceRefPos = this.shape.dockers.first().referencePoint;
				this.targetRefPos = this.shape.dockers.last().referencePoint;

			} else if (this.edge) {
				this.sourceRefPos = this.edge.dockers.first().referencePoint;
				this.targetRefPos = this.edge.dockers.last().referencePoint;
			}
		} else {
			var containedStencil = this.containedStencil;
			var connectedShape = this.connectedShape;
			var bc = connectedShape.bounds;
			var bs = this.shape.bounds;

			var pos = bc.center();
			if (containedStencil.defaultAlign() === "north") {
				pos.y -= (bc.height() / 2)
						+ ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET
						+ (bs.height() / 2);
			} else if (containedStencil.defaultAlign() === "northeast") {
				pos.x += (bc.width() / 2)
						+ ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER
						+ (bs.width() / 2);
				pos.y -= (bc.height() / 2)
						+ ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER
						+ (bs.height() / 2);
			} else if (containedStencil.defaultAlign() === "southeast") {
				pos.x += (bc.width() / 2)
						+ ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER
						+ (bs.width() / 2);
				pos.y += (bc.height() / 2)
						+ ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER
						+ (bs.height() / 2);
			} else if (containedStencil.defaultAlign() === "south") {
				pos.y += (bc.height() / 2)
						+ ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET
						+ (bs.height() / 2);
			} else if (containedStencil.defaultAlign() === "southwest") {
				pos.x -= (bc.width() / 2)
						+ ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER
						+ (bs.width() / 2);
				pos.y += (bc.height() / 2)
						+ ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER
						+ (bs.height() / 2);
			} else if (containedStencil.defaultAlign() === "west") {
				pos.x -= (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET
						+ (bs.width() / 2);
			} else if (containedStencil.defaultAlign() === "northwest") {
				pos.x -= (bc.width() / 2)
						+ ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER
						+ (bs.width() / 2);
				pos.y -= (bc.height() / 2)
						+ ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET_CORNER
						+ (bs.height() / 2);
			} else {
				pos.x += (bc.width() / 2) + ORYX.CONFIG.SHAPEMENU_CREATE_OFFSET
						+ (bs.width() / 2);
			}

			// Move shape to the new position
			this.shape.bounds.centerMoveTo(pos);

			// Move all dockers of a node to the position
			if (this.shape instanceof ORYX.Core.Node) {
				(this.shape.dockers || []).each(function(docker) {
							docker.bounds.centerMoveTo(pos);
						});
			}

			// this.shape.update();
			this.position = pos;

			if (this.edge) {
				this.sourceRefPos = this.edge.dockers.first().referencePoint;
				this.targetRefPos = this.edge.dockers.last().referencePoint;
			}
		}

		this.facade.getCanvas().update();
		this.facade.updateSelection();

	},
	rollback : function() {
		this.facade.deleteShape(this.shape);
		if (this.edge) {
			this.facade.deleteShape(this.edge);
		}
		// this.currentParent.update();
		this.facade.setSelection(this.facade.getSelection().without(this.shape,
				this.edge));
	}
});
